package redis_ser

import (
	"5title_gin/global"
	"5title_gin/pkg/utils/jwts"
	"context"
	"errors"
	"fmt"
	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"
	"strconv"
	"time"
)

// Logout 登出（加入黑名单）
func Logout(token string, userID uint) string {
	// 加入 Access Token 黑名单
	key := "token:logout_" + jwts.HashToken(token)
	result, _ := global.Redis.Get(context.Background(), key).Result()
	if result != "" {
		return "别不要脸"
	}
	claims, err := jwts.CheckToken(token)
	if err != nil {
		// 如果解析失败，默认 1 小时
		global.Redis.Set(context.Background(), key, "1", time.Hour)
		return ""
	} else {
		// 计算 Token 还有多久过期
		expire := time.Until(claims.ExpiresAt.Time)
		if expire <= 0 {
			expire = time.Hour
		}
		global.Redis.Set(context.Background(), key, "1", expire)

		// 删除 Access Token
		InvalidateAccessToken(userID)

		// 删除 Refresh Token
		InvalidateRefreshToken(userID)
		return ""
	}
}

// HasLogout 检查 Token 是否已登出（Redis 黑名单）
func HasLogout(token string) bool {
	key := "token:logout_" + jwts.HashToken(token)
	_, err := global.Redis.Get(context.Background(), key).Result()
	if errors.Is(err, redis.Nil) { // Key 不存在，说明 Token 没有登出
		return false
	} else if err != nil { // Redis 连接错误，记录日志
		zap.S().Error("redis get logout token error:", zap.Error(err))
		return false
	}
	return true // Key 存在，说明 Token 在黑名单中
}

// StoreAccessToken 存储session
func StoreAccessToken(userID uint, accessToken string) error {
	key := fmt.Sprintf("token:access_token_%d", userID)
	hashedToken := jwts.HashToken(accessToken)
	_, err := global.Redis.Set(context.Background(), key, hashedToken, time.Hour*2).Result()
	if err != nil {
		zap.S().Error("redis set access token error:", zap.Error(err))
	}
	return err
}

// StoreRefreshToken 存储 Refresh Token
func StoreRefreshToken(userID uint, refreshToken string) error {
	key := fmt.Sprintf("token:refresh_token_%d", userID)
	expire := time.Hour * 24 * 7                // 7 天
	hashedToken := jwts.HashToken(refreshToken) // 存入哈希值
	_, err := global.Redis.Set(context.Background(), key, hashedToken, expire).Result()
	if err != nil {
		zap.S().Error("redis set refresh token error:", zap.Error(err))
	}
	return err
}

func GetRefreshToken(userID uint) (string, error) {
	key := fmt.Sprintf("token:refresh_token_%d", userID)
	hashedToken, err := global.Redis.Get(context.Background(), key).Result()
	if errors.Is(err, redis.Nil) {
		return "", fmt.Errorf("refresh token not found in redis")
	} else if err != nil {
		zap.S().Error("redis get refresh token error:", zap.Error(err))
	}
	return hashedToken, err
}

// ValidateRefreshToken 校验 Refresh Token 是否有效
func ValidateRefreshToken(userID uint, refreshToken string) bool {
	key := fmt.Sprintf("token:refresh_token_%d", userID)
	storedHashedToken, err := global.Redis.Get(context.Background(), key).Result()
	if errors.Is(err, redis.Nil) {
		return false // Token 过期或不存在
	} else if err != nil {
		zap.S().Error("redis get refresh token error:", zap.Error(err))
		return false
	}
	return storedHashedToken == jwts.HashToken(refreshToken)
}

// InvalidateAccessToken 让 Refresh Token 失效（登出时调用）
func InvalidateAccessToken(userID uint) {
	key := fmt.Sprintf("token:access_token_%d", userID)
	global.Redis.Del(context.Background(), key)
}

// InvalidateRefreshToken 让 Refresh Token 失效（登出时调用）
func InvalidateRefreshToken(userID uint) {
	key := fmt.Sprintf("token:refresh_token_%d", userID)
	global.Redis.Del(context.Background(), key)
}

func SetUserInfo(userID uint) error {
	key := fmt.Sprintf("token:user_info:%d", userID)
	err := global.Redis.Set(context.Background(), key, userID, 24*time.Hour).Err() // 设置 24 小时过期
	if err != nil {
		zap.S().Error("redis set user info error:", zap.Error(err))
		return err
	}
	return nil
}

func GetUserInfo(userID uint) (uint, error) {
	key := fmt.Sprintf("token:user_info:%d", userID)
	userIdStr, err := global.Redis.Get(context.Background(), key).Result()
	if err != nil {
		if err == redis.Nil {
			return 0, fmt.Errorf("user info not found in redis")
		}
		zap.S().Error("redis get user info error:", zap.Error(err))
		return 0, err
	}
	userId, err := strconv.ParseUint(userIdStr, 10, 32)
	if err != nil {
		zap.S().Error("parse user id error:", zap.Error(err))
		return 0, err
	}
	return uint(userId), nil
}
